/*
 * Description: thread pool class.
 */

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <atomic>
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>

namespace COMMON {
class ThreadPool {
public:
    explicit ThreadPool(size_t threads);

    template<class F, class... Args>
    auto Enqueue(F &&f, Args &&... args) -> std::future<typename std::result_of<F(Args...)>::type>;

    ~ThreadPool();

private:
    std::vector<std::thread> m_workers {};
    std::queue<std::function<void()>> m_tasks {};

    std::mutex m_queueMutex {};
    std::condition_variable m_condition {};
    std::atomic<bool> m_stopped {false};
};

template<class F, class... Args>
auto ThreadPool::Enqueue(F &&f, Args &&... args) -> std::future<typename std::result_of<F(Args...)>::type>
{
    using RETURN_TYPE = typename std::result_of<F(Args...)>::type;

    auto task = std::make_unique<std::packaged_task<RETURN_TYPE()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...));

    auto result = task->get_future();
    {
        std::unique_lock<std::mutex> lock(m_queueMutex);

        auto realTask = task.release();
        m_tasks.emplace([realTask]() {
            (*realTask)();
            delete realTask;
        });
    }
    m_condition.notify_one();
    return result;
}
}
#endif // THREADPOOL_H